﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Security;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace ByteSite.WinClient.Common
{
    public class WebRequestHelper
    {
        private const string userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36  ByteSite/1.0.0";

        #region HttpPost
        public static string HttpPost(WebRequestOption options)
        {
            if (options.SecurityProtocol != SecurityProtocolType.SystemDefault)
            {
                ServicePointManager.SecurityProtocol = options.SecurityProtocol;
            }
            HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(options.Url);
            myRequest.Method = "POST";
            if (options.CC != null)
            {
                myRequest.CookieContainer = options.CC;
            }
            if (options.Headers.Count > 0)
            {
                foreach (var item in options.Headers)
                {
                    myRequest.Headers.Add(item.Key, item.Value);
                }
            }
            if (options.ContentType != null)
            {
                myRequest.ContentType = options.ContentType;
            }
            else
            {
                //myRequest.ContentType = "application/x-www-form-urlencoded";
                myRequest.ContentType = "application/json";
            }
            myRequest.UserAgent = userAgent;

            byte[] payload = null;
            string formData = GetFormData(options.Paras);
            if (!string.IsNullOrEmpty(formData))
            {
                payload = Encoding.UTF8.GetBytes(formData);
                myRequest.ContentLength = payload.Length;
            }
            if (options.MyProxy != null)
            {
                myRequest.Proxy = options.MyProxy;
            }
            using (Stream mySream = myRequest.GetRequestStream())
            {
                if (payload != null && payload.Length > 0)
                {
                    mySream.Write(payload, 0, payload.Length);
                }
            }

            try
            {
                using (HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse())
                {
                    using (StreamReader myReader = new StreamReader(myResponse.GetResponseStream(), options.MyEncoding))
                    {
                        string responseText = myReader.ReadToEnd();
                        return responseText;
                    }
                }
            }
            catch (Exception ex)
            {
                options.MyException = ex;
                return null;
            }
        }

        public static async Task<string> HttpPostAsync(WebRequestOption options)
        {
            if (options.SecurityProtocol != SecurityProtocolType.SystemDefault)
            {
                ServicePointManager.SecurityProtocol = options.SecurityProtocol;
            }
            HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(options.Url);
            myRequest.Method = "POST";
            if (options.CC != null)
            {
                myRequest.CookieContainer = options.CC;
            }
            if (options.Headers.Count > 0)
            {
                foreach (var item in options.Headers)
                {
                    myRequest.Headers.Add(item.Key, item.Value);
                }
            }
            if (options.ContentType != null)
            {
                myRequest.ContentType = options.ContentType;
            }
            else
            {
                myRequest.ContentType = "application/x-www-form-urlencoded";
                //myRequest.ContentType = "application/json";
            }
            myRequest.UserAgent = userAgent;

            byte[] payload = null;
            string formData = GetFormData(options.Paras);
            if (!string.IsNullOrEmpty(formData))
            {
                payload = Encoding.UTF8.GetBytes(formData);
                myRequest.ContentLength = payload.Length;
            }
            if (options.MyProxy != null)
            {
                myRequest.Proxy = options.MyProxy;
            }
            using (Stream mySream = myRequest.GetRequestStream())
            {
                if (payload != null && payload.Length > 0)
                {
                    mySream.Write(payload, 0, payload.Length);
                }
            }

            try
            {
                using (HttpWebResponse myResponse = (HttpWebResponse)await myRequest.GetResponseAsync())
                {
                    using (StreamReader myReader = new StreamReader(myResponse.GetResponseStream(), options.MyEncoding))
                    {
                        string responseText = myReader.ReadToEnd();
                        return responseText;
                    }
                }
            }
            catch (Exception ex)
            {
                options.MyException = ex;
                return null;
            }
        }

        public static async Task<string> HttpPostJsonAsync(WebRequestOption options)
        {
            if (options.SecurityProtocol != SecurityProtocolType.SystemDefault)
            {
                ServicePointManager.SecurityProtocol = options.SecurityProtocol;
            }
            HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(options.Url);
            myRequest.Method = "POST";
            if (options.CC != null)
            {
                myRequest.CookieContainer = options.CC;
            }
            if (options.Headers.Count > 0)
            {
                foreach (var item in options.Headers)
                {
                    myRequest.Headers.Add(item.Key, item.Value);
                }
            }
            myRequest.ContentType = "application/json";
            myRequest.UserAgent = userAgent;

            byte[] payload = null;
            string jsonData = options.Paras.ToString();
            if (!string.IsNullOrEmpty(jsonData))
            {
                payload = Encoding.UTF8.GetBytes(jsonData);
                myRequest.ContentLength = payload.Length;
            }
            if (options.MyProxy != null)
            {
                myRequest.Proxy = options.MyProxy;
            }
            using (Stream mySream = myRequest.GetRequestStream())
            {
                if (payload != null && payload.Length > 0)
                {
                    mySream.Write(payload, 0, payload.Length);
                }
            }

            try
            {
                using (HttpWebResponse myResponse = (HttpWebResponse)await myRequest.GetResponseAsync())
                {
                    using (StreamReader myReader = new StreamReader(myResponse.GetResponseStream(), options.MyEncoding))
                    {
                        string responseText = myReader.ReadToEnd();
                        return responseText;
                    }
                }
            }
            catch (WebException ex)
            {
                var res = (HttpWebResponse)ex.Response;
                using (StreamReader myReader = new StreamReader(res.GetResponseStream(), options.MyEncoding))
                {
                    string responseText = myReader.ReadToEnd();
                    return responseText;
                }
            }
            catch (Exception ex)
            {
                options.MyException = ex;
                return null;
            }
        }


        public static async Task<string> HttpPostFileAsync(WebRequestOption options)
        {
            if (options.SecurityProtocol != SecurityProtocolType.SystemDefault)
            {
                ServicePointManager.SecurityProtocol = options.SecurityProtocol;
            }
            HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(options.Url);
            myRequest.Method = "POST";
            string boundary = DateTime.Now.Ticks.ToString("X"); // 随机分隔线

            if (options.CC != null)
            {
                myRequest.CookieContainer = options.CC;
            }
            if (options.Headers.Count > 0)
            {
                foreach (var item in options.Headers)
                {
                    myRequest.Headers.Add(item.Key, item.Value);
                }
            }
            if (options.ContentType != null)
            {
                myRequest.ContentType = options.ContentType;
            }
            else
            {
                myRequest.ContentType = "multipart/form-Data;charset=utf-8;boundary=" + boundary;
                //myRequest.ContentType = "application/json";
            }
            myRequest.UserAgent = userAgent;
            if (options.MyProxy != null)
            {
                myRequest.Proxy = options.MyProxy;
            }

            byte[] itemBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");
            byte[] endBoundaryBytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
            
            StringBuilder sbHeader = new StringBuilder(string.Format("Content-Disposition:form-Data;name=\"file\";filename=\"{0}\"\r\nContent-Type:application/octet-stream\r\n\r\n", options.FileName));
            byte[] payloadFile = Encoding.UTF8.GetBytes(sbHeader.ToString());
            byte[] payload = null;
            string formData = GetFormData(options.Paras);
            if (!string.IsNullOrEmpty(formData))
            {
                payload = Encoding.UTF8.GetBytes(formData);
            }

            

            FileStream fs = new FileStream(options.FilePath, FileMode.Open, FileAccess.Read);
            byte[] bArr = new byte[fs.Length];
            fs.Read(bArr, 0, bArr.Length);
            fs.Close();

            using (Stream mySream = myRequest.GetRequestStream())
            {
                if (payload != null)
                {
                    mySream.Write(itemBoundaryBytes, 0, itemBoundaryBytes.Length);
                    mySream.Write(payload, 0, payload.Length);
                }
                mySream.Write(itemBoundaryBytes, 0, itemBoundaryBytes.Length);
                mySream.Write(payloadFile, 0, payloadFile.Length);
                mySream.Write(bArr, 0, bArr.Length);

                mySream.Write(endBoundaryBytes, 0, endBoundaryBytes.Length);

            }
            try
            {
                using (HttpWebResponse myResponse = (HttpWebResponse)await myRequest.GetResponseAsync())
                {
                    using (StreamReader myReader = new StreamReader(myResponse.GetResponseStream(), options.MyEncoding))
                    {
                        string responseText = myReader.ReadToEnd();
                        return responseText;
                    }
                }
            }
            catch (Exception ex)
            {
                options.MyException = ex;
                return null;
            }






            

            
        }

        #endregion

        #region HttpGet
        public static string HttpGet(WebRequestOption options)
        {
            string formData = GetFormData(options.Paras);
            if (options.SecurityProtocol != SecurityProtocolType.SystemDefault)
            {
                ServicePointManager.SecurityProtocol = options.SecurityProtocol;
            }
            string url = string.IsNullOrEmpty(formData) ? options.Url : options.Url + "?" + formData;
            HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(url);
            myRequest.Method = "GET";
            if (options.Headers.Count > 0)
            {
                foreach (var item in options.Headers)
                {
                    myRequest.Headers.Add(item.Key, item.Value);
                }
            }
            if (options.CC != null)
            {
                myRequest.CookieContainer = options.CC;
            }
            myRequest.UserAgent = userAgent;
            if (url.StartsWith("https://"))
            {
                //myRequest.ContentType = "application/x-www-form-urlencoded";
                myRequest.ContentType = "application/json";
            }
            myRequest.Accept = "*/*";
            myRequest.KeepAlive = true;
            if (options.MyProxy != null)
            {
                myRequest.Proxy = options.MyProxy;
            }
            try
            {
                using (HttpWebResponse myResponse = (HttpWebResponse)myRequest.GetResponse())
                {
                    using (Stream sResponse = myResponse.GetResponseStream())
                    {
                        using (StreamReader myReader = new StreamReader(sResponse, options.MyEncoding))
                        {
                            options.Status = myResponse.StatusCode;
                            string responseText = myReader.ReadToEnd();
                            return responseText;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                options.MyException = ex;
                return null;
            }
        }

        public static async Task<string> HttpGetAsync(WebRequestOption options)
        {
            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);

            string formData = GetFormData(options.Paras);
            if (options.SecurityProtocol != SecurityProtocolType.SystemDefault)
            {
                ServicePointManager.SecurityProtocol = options.SecurityProtocol;
            }
            string url = string.IsNullOrEmpty(formData) ? options.Url : options.Url + "?" + formData;
            HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(url);
            myRequest.Method = "GET";
            if (options.Headers.Count > 0)
            {
                foreach (var item in options.Headers)
                {
                    myRequest.Headers.Add(item.Key, item.Value);
                }
            }
            if (options.CC != null)
            {
                myRequest.CookieContainer = options.CC;
            }
            myRequest.UserAgent = userAgent;
            if (url.StartsWith("https://"))
            {
                //myRequest.ContentType = "application/x-www-form-urlencoded";
                myRequest.ContentType = "application/json";
            }
            myRequest.Accept = "*/*";
            myRequest.KeepAlive = true;
            if (options.MyProxy != null)
            {
                myRequest.Proxy = options.MyProxy;
            }
            try
            {
                using (HttpWebResponse myResponse = (HttpWebResponse)await myRequest.GetResponseAsync())
                {
                    using (Stream sResponse = myResponse.GetResponseStream())
                    {
                        using (StreamReader myReader = new StreamReader(sResponse, options.MyEncoding))
                        {
                            options.Status = myResponse.StatusCode;
                            string responseText = myReader.ReadToEnd();
                            return responseText;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                options.MyException = ex;
                return null;
            }
        }
        #endregion

        public static async Task<string> ExecQueryAsync(string url)
        {
            using (HttpClient httpClient = new HttpClient())
            {
                HttpResponseMessage response = await httpClient.GetAsync(url);
                response.EnsureSuccessStatusCode();
                string resultStr = await response.Content.ReadAsStringAsync();
                return resultStr;
            }
        }

        private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
        {
            return true;
        }

        #region 帮助方法
        public static string GetFormData(object paras)
        {
            StringBuilder formData = new StringBuilder();
            if (paras != null)
            {
                Type t = paras.GetType();
                if (t.Name.Contains("Dictionary"))
                {
                    foreach (KeyValuePair<String, String> kvp in paras as Dictionary<string, string>)
                    {
                        if (formData.ToString() == "")
                        {
                            formData.Append(kvp.Key + "=" + kvp.Value);
                        }
                        else
                        {
                            formData.Append("&" + kvp.Key + "=" + kvp.Value);
                        }
                    }
                }
                else if (t.Name == "String")
                {
                    formData.Append(paras.ToString());
                }
                else
                {
                    foreach (PropertyInfo pi in t.GetProperties())
                    {
                        var jsonProperty = pi.CustomAttributes.SingleOrDefault(p => p.AttributeType.FullName == "Newtonsoft.Json.JsonPropertyAttribute");
                        string name = jsonProperty == null ? pi.Name : jsonProperty.ConstructorArguments[0].Value.ToString();
                        object val = pi.GetValue(paras, null);

                        if (formData.ToString() == "")
                        {
                            formData.Append(name + "=" + val);
                        }
                        else
                        {
                            formData.Append("&" + name + "=" + val);
                        }
                    }
                }
            }
            return formData.ToString();
        }
        #endregion
    }
    public class WebRequestOption
    {
        public string Url { get; set; }
        /// <summary>
        /// 可以是匿名对象， 可以是字典
        /// </summary>
        public object Paras { get; set; }
        public Dictionary<string, string> Headers = new Dictionary<string, string>();
        public CookieContainer CC { get; set; }
        public Encoding MyEncoding { get; set; }
        public WebProxy MyProxy { get; set; }
        public HttpStatusCode Status { get; set; }
        public Exception MyException { get; set; }
        public SecurityProtocolType SecurityProtocol { get; set; }
        public string ContentType { get; set; }
        public string FileName { get; set; }
        public string FilePath { get; set; }

        /// <summary>
        /// 没有任何关于SecurityProtocold的默认设置
        /// </summary>
        public WebRequestOption()
        {
            if (this.MyEncoding == null)
            {
                this.MyEncoding = Encoding.UTF8;
            }
        }
        /// <summary>
        /// htts协议将默认设置为Tls1.0
        /// </summary>
        /// <param name="url"></param>
        /// <param name="paras"></param>
        public WebRequestOption(string url, object paras = null)
        {
            this.Url = url;
            if (paras != null)
            {
                this.Paras = paras;
            }
            if (this.MyEncoding == null)
            {
                this.MyEncoding = Encoding.UTF8;
            }
            if (this.Url.StartsWith("https") && this.SecurityProtocol == SecurityProtocolType.SystemDefault)
            {
                this.SecurityProtocol = SecurityProtocolType.Tls12;
            }
        }
    }
}


